﻿using Silk.NET.Input;
using Silk.NET.OpenGL;
using System.Drawing;


namespace Test
{

	internal class Learn_01_HelloOpenGL : LearnClass
	{
		private static GL _gl;

		// 顶点数组对象可以存储顶点属性信息，在gl核心模式中必须绑定一个vao才能执行绘制
		private static uint _vao;
		private static uint _vbo, _ebo;
        const string vertexCode = @"
#version 330 core

layout (location = 0) in vec3 aPosition;

void main()
{
	gl_Position = vec4(aPosition, 1.0);
}";
        const string fragmentCode = @"
#version 330 core

out vec4 out_color;

void main()
{
    out_color = vec4(1.0, 0.5, 0.2, 1.0);
}";
        private static uint _shaderProgram;



        string LearnClass.Name => "01: Basic OpenGL";

		void LearnClass.OnKeyDown(IKeyboard keyboard, Key key, int keyCode)
		{
		}

		void LearnClass.OnLoad()
		{
			if (Program._Window == null)
			{
				throw new Exception("Need call after _Window init");
			}

			_gl = Program._Window.CreateOpenGL();
			_gl.ClearColor(Color.CornflowerBlue);

            // 绑定VAO
			_vao = _gl.GenVertexArray();
			_gl.BindVertexArray(_vao);

			float[] vertices =
			{
				 0.5f,  0.5f, 0.0f,
				 0.5f, -0.5f, 0.0f,
				-0.5f, -0.5f, 0.0f,
				-0.5f,  0.5f, 0.0f
			};
			uint[] indices =
			{
				0u, 1u, 3u,
				1u, 2u, 3u
			};

            // 绑定buffer
			_vbo = _gl.GenBuffer();
			_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
            _ebo = _gl.GenBuffer();
            _gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, _ebo);

            unsafe
            {
				fixed (float* buf = vertices)
					_gl.BufferData(BufferTargetARB.ArrayBuffer, (nuint)(vertices.Length * sizeof(float)), buf, BufferUsageARB.StaticDraw);
                fixed (uint* buf = indices)
                    _gl.BufferData(BufferTargetARB.ElementArrayBuffer, (nuint)(indices.Length * sizeof(uint)), buf, BufferUsageARB.StaticDraw);

                // 设置顶点属性指针 可以理解为这是在VAO上进行的操作
                const uint positionLoc = 0;
                _gl.EnableVertexAttribArray(positionLoc);
                _gl.VertexAttribPointer(positionLoc, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), (void*)0);
            }

            // 解绑buffer，可以避免误操作错误的buffer导致的bug
            // 先解绑VAO，再解绑buffer，否则将会是把buffer从VAO解绑
            _gl.BindVertexArray(0);
            _gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
            _gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, 0);

            // 创建shader
            if (!CreateShaderProgram(vertexCode, fragmentCode, out _shaderProgram))
			{
                throw new Exception("Create Shader Error");
			}


        }

        void LearnClass.OnRender(double deltaTime)
		{
			_gl.Clear(ClearBufferMask.ColorBufferBit);

            _gl.BindVertexArray(_vao);
            _gl.UseProgram(_shaderProgram);
            unsafe
            {
                _gl.DrawElements(PrimitiveType.Triangles, 6, DrawElementsType.UnsignedInt, (void*)0);
            }
        }

        void LearnClass.OnUnload()
		{
		}

		void LearnClass.OnUpdate(double deltaTime)
		{
		}


		static public bool CreateShaderProgram(string vertCode, string fragCode, out uint program)
		{
			program = 0;
            var error = "";

            uint vertexShader = _gl.CreateShader(ShaderType.VertexShader);
            _gl.ShaderSource(vertexShader, vertCode);
            uint fragmentShader = _gl.CreateShader(ShaderType.FragmentShader);
            _gl.ShaderSource(fragmentShader, fragCode);

            _gl.CompileShader(vertexShader);
            _gl.GetShader(vertexShader, ShaderParameterName.CompileStatus, out int vStatus);
            if (vStatus != (int)GLEnum.True)
                error += "Vertex shader failed to compile: " + _gl.GetShaderInfoLog(vertexShader) + "\n";

            _gl.CompileShader(fragmentShader);
            _gl.GetShader(fragmentShader, ShaderParameterName.CompileStatus, out int fStatus);
            if (fStatus != (int)GLEnum.True)
                error += "Fragment shader failed to compile: " + _gl.GetShaderInfoLog(fragmentShader) + "\n";

            if (error != "")
            {
                Console.WriteLine(error, ConsoleColor.Red);
                _gl.DeleteShader(vertexShader);
                _gl.DeleteShader(fragmentShader);
                return false;
            }

            program = _gl.CreateProgram();
            _gl.AttachShader(program, vertexShader);
            _gl.AttachShader(program, fragmentShader);

            _gl.LinkProgram(program);

            _gl.GetProgram(program, ProgramPropertyARB.LinkStatus, out int lStatus);
            if (lStatus != (int)GLEnum.True)
                error += "Program failed to link: " + _gl.GetProgramInfoLog(program) + "\n";

            if (error != "")
            {
                Console.WriteLine(error, ConsoleColor.Red);
                _gl.DeleteShader(vertexShader);
                _gl.DeleteShader(fragmentShader);
                return false;
            }

            _gl.DetachShader(program, vertexShader);
            _gl.DetachShader(program, fragmentShader);
            _gl.DeleteShader(vertexShader);
            _gl.DeleteShader(fragmentShader);


            return true;
        }
	}
}
